package org.netty.mvc.spring;

import org.netty.mvc.annotation.NettyController;
import org.netty.mvc.annotation.NettyControllerScans;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @description:
 * @author: Jun jie Yi
 * @date: 2023-10-22 21:31
 */
public class ApplicationContext {

    //一级缓存，单列池
    public Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();

    //用来存储实列对象的注解定义信息
    public Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    public ApplicationContext(Class<?> configClass) {
        //扫描操作configClass为配置类
        scan(configClass);
        //实列化对象
        createBeanInstance();
    }

    private void scan(Class<?> configClass) {
        //解析@NettyControllerScans注解->扫描路径->扫描操作
        if (configClass.isAnnotationPresent(NettyControllerScans.class)) {
            NettyControllerScans nettyControllerScans = configClass.getDeclaredAnnotation(NettyControllerScans.class);
            //扫描，需要用到类加载器
            //1.Bootstrap--->jre/lib
            //2.Ext--------->jre/ext/lib
            //3.App--------->classpath
            //获取configClass的类加载器
            ClassLoader classLoader = configClass.getClassLoader();
            //只能识别英文路径
            URL url = configClass.getProtectionDomain().getCodeSource().getLocation();
            //文件路径
            String filePath = url.getFile();
            System.out.println("加载文件位置：" + filePath);

            String[] paths = nettyControllerScans.paths();
            for (String path : paths) {
                path = path.replace(".", "/");
                System.out.println("映射文件位置：" + path);
                if (filePath.endsWith(".jar")) {//jar包中运行
                    try {
                        JarFile jarFile = new JarFile(filePath);
                        Enumeration<JarEntry> entries = jarFile.entries();
                        while (entries.hasMoreElements()) {
                            JarEntry jarEntry = entries.nextElement();
                            String jarClassPath = jarEntry.getName();
                            if (jarClassPath.startsWith(path) && jarClassPath.endsWith(".class")) {
                                String className = jarClassPath.replace(".class", "").replace("/", ".");
                                loadClassByName(className, classLoader);
                            }
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                } else {//非jar包中运行
                    File file = new File(filePath + path);
                    //类路径
                    if (file.isDirectory()) {
                        File[] files = file.listFiles();
                        if (files.length > 0) {
                            for (File f : files) {
                                String fileName = f.getAbsolutePath();
                                String path1 = f.getPath();
                                //保证是class文件
                                if (fileName.endsWith(".class")) {
                                    String prex = path.split("/")[0];
                                    String className = fileName.substring(fileName.indexOf(prex), fileName.indexOf(".class"))
                                            .replace("\\", ".");
                                    loadClassByName(className, classLoader);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    private void loadClassByName(String className, ClassLoader classLoader) {
        try {
            Class<?> clazz = classLoader.loadClass(className);
            //判断是否有标注此注解
            if (clazz.isAnnotationPresent(NettyController.class)) {
                //表示当前这个类是一个bean（是否要生成bean对象跟bean的作用域有关系）
                //解析类生成一个BeanDefinition对象，判断是singleton的bean还是prototype的bean
                //引入BeanDefinition
                NettyController nettyController = clazz.getDeclaredAnnotation(NettyController.class);
                String beanName = nettyController.value();
                //默认bean名称为类名
                if ("".equals(beanName)) {
                    String[] classNameArr = clazz.getName().split("\\.");
                    beanName = lowerFirst(classNameArr[classNameArr.length - 1]);
                }
                //解析bean的定义存储到BeanDefinition对象当中
                BeanDefinition beanDefinition = new BeanDefinition();
                //设置beanDefinition的clazz
                beanDefinition.setClazz(clazz);
                //默认全为单列对象
                beanDefinition.setScope("singleton");
                //将这个bean的定义信息存储到beanDefinitionMap对象当中
                beanDefinitionMap.put(beanName, beanDefinition);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void createBeanInstance() {
        //将单列bean都创建好，放入一级缓存当中
        for (Map.Entry<String, BeanDefinition> beanDefinitionEntry : beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            BeanDefinition beanDefinition = beanDefinitionEntry.getValue();
            if (beanDefinition.getScope().equals("singleton")) {
                //获取到单列bean
                Object bean = createBean(beanName, beanDefinition);
                if (!singletonObjects.containsKey(beanName)) {
                    singletonObjects.put(beanName, bean);
                }
            }
        }
    }

    public Object createBean(String beanName, BeanDefinition beanDefinition) {
        Class<?> clazz = beanDefinition.getClazz();
        try {
            Object instance = clazz.getDeclaredConstructor().newInstance();
            //依赖注入功能未实现
            return instance;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String lowerFirst(String str) {
        char[] cs = str.toCharArray();
        cs[0] += 32;
        return String.valueOf(cs);
    }

    public Map<String, Object> getSingletonObjects() {
        return singletonObjects;
    }

    public Map<String, BeanDefinition> getBeanDefinitionMap() {
        return beanDefinitionMap;
    }

}
